聊聊提高代码质量

什么样的代码才是好的——李仙鹏

我所理解的好代码

  • 代码规范——可读性

  • 代码耦合低——可扩展性、可移植性

    • 尽量采用接口实现,减少继承
    • 通用功能尽量抽取作为一个独立的方法,避免重复造轮子
    • 设计模式并不能提高代码执行效率,但容易对代码进行模块切分,从而进行代码的解耦合。设计模式也可以算是程序员的一种通用“语言“,方便程序员之间的沟通.
      • 根据不同OS或者语言特性,从大的架构上遵循MVC或者MVP或者其它类似的层次分明的设计模式。
  • 严格的code review

    • 能够发现潜在的bug、不合理的实现和是否遵从代码规范
    • 有助于代码提交人员对自身代码质量的要求——面子问题
  • 性能

以上这些点都是为了程序的健壮性、可扩展性、可移植性。最终目标,在核心开发人员变动或者产品需求变动后,都不会对代码维护、版本迭代和程序稳定性造成重大影响。

51offer-v2.5.0重写中,我们是如何做的

  • 整体架构上采用MVP模式

    • V和P通过接口实现交互,M只被P处理,P处理完后通过接口反馈给V
      MVP
  • 代码可移植性和可扩展性

    • 按照是否通用原则,命名包名——非通用包名,放在offer包下;通用功能模块,放在非offer包下。这样有利于快速移植,或者打成JAR包
    • 接口实现,减少继承——在类的继承中,减少继承体系中每层父类的职责范围
  • HTTP请求

    • 底层请求采用OkHttpOkHttp默认实现的功能为:
      • 支持协议——HTTP/2SPDY, 可以合并多个到同一个主机的请求
        • 使用连接池技术减少请求的延迟
        • 使用GZIP压缩减少传输的数据量
        • 报文缓存响应,避免重复的网络请求
  • json解析

  • 图片加载

    • 采用Android-Universal-Image-Loader

      • 多线程图片加载
      • 可自定义的加载器
      • 可自定义的图片显示回调
      • 图片三级缓存——内存和硬盘缓存的图片通过多种数据结构管理
      • 监听加载过程
    • bitmap显示质量设置为RGB_565——一个像素需要16位表示,Android中默认为ARGB_8888——一个像素需要32位表示。这种方式在基本保障图片质量要求的同时,还能够大大减少手机的内存占用

  • 数据存储

    • 由于目前客户端还未涉及到大量数据和离线加载模式,所以我们暂不使用数据库(以后在做IM或者离线加载时,会考虑使用ORM来访问数据库),综合速度考虑使用Android的SharedPreference

组件通信库EventBus - 吴强

EventBus是一个Android端优化的publish/subscribe消息总线,简化了应用程序内各组件间、组件与后台线程间的通信。

三个主要元素:

  • Event:事件

    Event可以是任意类型的对象

  • Subscriber:事件订阅者,接收特定的事件

    在EventBus中,使用约定来指定事件订阅者以简化使用。即所有事件订阅都都是以onEvent开头的函数,具体来说,函数的名字是onEvent,onEventMainThread,onEventBackgroundThread,onEventAsync这四个

  • Publisher:事件发布者,用于通知Subscriber有事件发生

    可以在任意线程任意位置发送事件,直接调用EventBus的post(Object)方法,可以自己实例化EventBus对象,但一般使用默认的单例就好了:EventBus.getDefault(),根据post函数参数的类型,会自动调用订阅相应类型事件的函数。

ThreadMode

Subscriber函数的名字只能是那4个,因为每个事件订阅函数都是和一个ThreadMode相关联的,ThreadMode指定了会调用的函数。有以下四个ThreadMode:

  • PostThread:
    事件的处理在和事件的发送在相同的进程,所以事件处理时间不应太长,不然影响事件的发送线程,而这个线程可能是UI线程。对应的函数名是onEvent。
  • MainThread:
    事件的处理会在UI线程中执行。事件处理时间不能太长,这个不用说的,长了会ANR的,对应的函数名是onEventMainThread。
  • BackgroundThread:
    事件的处理会在一个后台线程中执行,对应的函数名是onEventBackgroundThread,虽然名字是BackgroundThread,事件处理是在后台线程,但事件处理时间还是不应该太长,因为如果发送事件的线程是后台线程,会直接执行事件,如果当前线程是UI线程,事件会被加到一个队列中,由一个线程依次处理这些事件,如果某个事件处理时间太长,会阻塞后面的事件的派发或处理。
  • Async:
    事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作,每个事件会开启一个线程(有线程池),但最好限制线程的数目。

注册事件与解除注册

  • 通过EventBus.getDefault().register方法可以向EventBus注册来订阅事件
  • 通过registerSticky可以注册Stick事件处理函数
  • 通过EventBus.getDefault().unregister方法解除EventBus事件订阅

Post事件

  • 直接调用EventBus.getDefault().post(Event)就可以发送事件,根据Event的类型就可以发送到相应事件的订阅者。
  • 当通过postSticky发送一个事件时,这个类型的事件的最后一次事件会被缓存起来,当有订阅者通过registerSticky注册时,会把之前缓存起来的这个事件直接发送给它

缺点

无法进程间通信,如果一个应用内有多个进程的话就没办法了

注意事项及要点

  • 同一个onEvent函数不能被注册两次,所以不能在一个类中注册同时还在父类中注册
  • 当Post一个事件时,这个事件类的父类的事件也会被Post。
  • Post的事件无Subscriber处理时会Post NoSubscriberEvent事件,当调用Subscriber失败时会Post SubscriberExceptionEvent事件。

其他

EventBus中还有个Util包,主要作用是可以通过AsyncExecutor执行一个Runnable,通过内部的RunnableEx(可以搜索异常的Runnable)当Runnable抛出异常时通过EventBus发消息显示错误对话框。

参考资料:快速Android开发系列通信篇之EventBus

如何提高代码质量——吴明

Alt text

  • code review
  • 命名规范
    • java命名规范:
      • java类:m+模块+功能(如:mLoginRegester)
      • 控件类:m+模块+功能+控件缩写(如:mLoginRegesterBtn)
      • 常量:模块+功能(如:SCHOOL_COUNT_MAX)
    • xml命名规范
      • id:模块+功能+控件缩写
    • color,string,dimen,drawable
    • 图片:模块功能备注_状态
    • 注释
  • 好的框架
    • okhttp
    • Gson
  • 设计模式:
  • 处理异常
  • 优化性能

  • 代码测试工具:静态代码分析

如何写一份好的代码 - 张超耀

数据结构和核心算法

  • 数据结构的重要性:低水平程序员总在考虑代码,高水平程序员总在考虑数据结构及其之间的关系

  • 数据结构决定算法,数据结构考虑清楚了,核心的算法自然就出来了,这就是关于每个类的每个方法如何实现的问题

功能实现

  • 思路确定后,实现过程也需要大量的构思活动。碰到比较熟悉有经验的领域,自然可以轻车熟路,但难免会有一些你不太熟悉的技术需要尝试。作为一个程序员,最大的挑战也是最大的乐趣所在,就是不断学习新的技术,没有这样的心态,很快就会落后。

  • 那么遇到不熟悉的技术怎么办?Demo先行,这样做的好处是把单个技术问题和其他潜在的bug隔离开来,便于快速学习新技术。否则,直接在项目里写代码出错以后,要判断问题的源头都要多费好几倍的精力。

测试

  • 测试很重要,设计测试用例就像开发时设计数据结构一样,也是很关键的。

代码可读性

  • 要想自己满意,代码的可读性一定要好。要做到一年后甚至几年后你拿到自己写的代码,还能很容易看明白当时的思路和实现。这就涉及到命名和注释的问题

  • 命名就像超市里的商品标签一样,要让看得人一目了然就知道这是个什么东西

  • 注释也是很重要的,它可以用来说明一段代码的作用,算法的设计思想,或者是方法调用的参数格式要求等

最后总结一下:

  • 技术水平是可以慢慢提高的,但是好的编程习惯需要从一开始就养成,它会让你在前进的道路上事半功倍,受益终生。

怎样写好的代码 - 曾铭

有两种方式构建软件设计:一种是把软件做得很简单以至于明显找不到缺陷;另一种是把它做得很复杂以至于找不到明显的缺陷
——C.A.R. Hoare

谁来写?角色的定义

  • 程序员 vs 工程师
  • 实现功能 vs 解决问题
  • 搭个帐篷 vs 照顾孩子

好的代码?

一个程序员更希望接手一个有bug但是看的懂的工程,还是一个没bug但是看不懂的工程?

  • 代码跟人聊天,解释做什么,注释解释为什么这么做,要注意什么
  • 面向接口而不仅仅面向对象

举例:在 APP 开发和 API 开发之间,面向 API 文档做开发

写的过程?(推荐开发过程)

  • 需求明确(理解来源及演进)
  • 整体设计(外在联系,临界条件,错误处理)
  • 实现(验证思路,解决问题及优化)切记不要拿到需求直接跳到这一步

好?目标

  • 可运行,可读,可维护,可测试
  • 参见 TDD,BDD